package com.lagou.edu.factory;

import com.lagou.edu.annotions.*;
import com.lagou.edu.utils.ConnectionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author oyz
 */
public class BeanFactory {

    /**
     * 缓存扫描到的全限定类名
     */
    static List<String> classNames = new ArrayList<>();

    /**
     * 缓存已经进行过依赖注入的信息
     */
    static List<String> fieldAlreadyProcessed = new ArrayList<>();

    /**
     * 1、读取解析xml,通过实例化对象把该对象放入一个map集合
     * 2、提供一个对外的key(id),来拿到这个对象
     */
    private static Map<String,Object> map = new HashMap<>();

    /**
     * 缓存已经进行过依赖注入的日期
     */
    static List<String> getFieldAlreadyProcessed = new ArrayList<>();


    static {
        // 加载xml,目的是为了扫描包
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            // 拿到元素节点
            Element rootElement = document.getRootElement();
            Element scanElement = (Element)rootElement.selectSingleNode("//bean-scan");
            String scanBackage = scanElement.attributeValue("base-packg");

            // 扫描注解
            doScan(scanBackage);
            // 实例化
            doInstance();
            // 维护依赖注入关系
            doAutoWired();
            // 维护事务
            doTransactional();


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static void doTransactional() {
        ProxyFactory proxyFactory= (ProxyFactory) map.get("proxyFactory");
        for(Map.Entry<String,Object> entry:map.entrySet()){
            String beanName = entry.getKey();
            Object o = entry.getValue();
            Class<?> aClass = o.getClass();
            // 判断该类是否有事务的注解
            if(aClass.isAnnotationPresent(OyzTransactional.class)){
                // 需要进行事务管理
                // 查看该类是否实现接口来判断用什么代理
                Class<?>[] interfaces = aClass.getInterfaces();
                // 说明有实现接口
                if(interfaces.length>0||interfaces!=null){
                    map.put(beanName,proxyFactory.getJdkProxy(o));
                }else{
                    map.put(beanName, proxyFactory.getCglibProxy(o));
                   ;
                }

            }
        }

    }

    private static void doAutoWired() {
        if(map.isEmpty()) return;

        // 遍历ioc中的所有对象,查看对象中的字段,看是否有autowired注解,需要维护一下类之间的关系
        for(Map.Entry<String,Object> entry:map.entrySet()){
            try {
                doObjectDependancy(entry.getValue());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }



    }

    /**
     * 维护嵌套依赖  A 可能依赖于 B ，B 可能依赖于 C ，C 可能又依赖于D，本方法主要维护一下嵌套依赖
     * @param object
     */
    private static void doObjectDependancy(Object object) throws IllegalAccessException {
        // 拿到该类的所有属性
        Field[] declaredFields = object.getClass().getDeclaredFields();
        if(declaredFields.length==0||declaredFields ==null) return;
        // 循坏该类的所有属性,目的是为了把需要的属性拿出来
        for (int i = 0; i < declaredFields.length; i++) {
           Field field =  declaredFields[i];
           // 判断有没有这个注解,从而进行维护
           if(!field.isAnnotationPresent(OyzAutowired.class)){
                continue;
           }
           // 避免死循环,参考三级缓存
            if(fieldAlreadyProcessed.contains(object.getClass().getName()+"."+field.getName())){
                continue;
            }

            Object dependObject = null;
            // 先按照声明的是接口去获取,如果声明不到就按照类名的小写获取
            dependObject = map.get(field.getType().getName());
            if(dependObject==null){
                dependObject = map.get(lowerFirst(field.getType().getSimpleName()));
            }

            // 记录下那个属性已经有autowired维护了的
            fieldAlreadyProcessed.add(object.getClass().getName()+"."+field.getName());
            // 迭代
            doObjectDependancy(dependObject);
            field.setAccessible(true);
            field.set(object,dependObject);


        }




    }

    /**
     * 通过反射实例化对象,暂不维护依赖注入关系
     */
    private static void doInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
         if(classNames.isEmpty()) return;

        for (int i = 0;i<classNames.size();i++) {
            String className = classNames.get(i);
            // 反射
            Class<?> aClass = Class.forName(className);
            // 只处理标注了的注解
            if(aClass.isAnnotationPresent(OyzService.class)||aClass.isAnnotationPresent(OyzRepository.class)
                ||aClass.isAnnotationPresent(OyzComponent.class)
            ){
                // 获取注解value的值
                String beanName = null;
                if(aClass.isAnnotationPresent(OyzService.class)){
                    beanName = aClass.getAnnotation(OyzService.class).value();
                }else if(aClass.isAnnotationPresent(OyzRepository.class)){
                    beanName = aClass.getAnnotation(OyzRepository.class).value();
                }else if (aClass.isAnnotationPresent(OyzComponent.class)){
                    beanName = aClass.getAnnotation(OyzComponent.class).value();
                }
                Object o = aClass.newInstance();
                if(beanName.isEmpty()){
                    // 调用小写类名的方法
                    beanName = lowerFirst(aClass.getSimpleName());
                }
                map.put(beanName,o);
                //把service层的接口，放一份到ioc容器,便于autowired注入
                Class<?>[] interfaces = aClass.getInterfaces();
                if(interfaces.length>0&&interfaces !=null){
                    for (int i1 = 0; i1 < interfaces.length; i1++) {
                       Class<?> anInterface = interfaces[i1];
                       // 以接口的全限定类型放入map中
                        map.put(anInterface.getName(),aClass.newInstance());
                    }
                }


            }else{
                continue;
            }
        }

    }

    private static String lowerFirst(String simpleName) {
        char[] chars = simpleName.toCharArray();
        if('A'<chars[0]&&chars[0]<'Z'){
            chars[0]+= 32;
        }
        return String.valueOf(chars);
    }

    /**
     * 扫描指定包下的注解
     * @param scanBackage
     */
    private static void doScan(String scanBackage) {
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath()+scanBackage.replaceAll("\\.","/");
        File file = new File(path);
        File[] files = file.listFiles();
        for (File file1 : files) {
            // 判断是否是一个文件夹
            if(file1.isDirectory()){
                // com.lagou.edu.controller
                doScan(scanBackage+"."+file1.getName());
            }else{
                String className = scanBackage+"."+file1.getName().replaceAll(".class","");
                classNames.add(className);
            }
        }

    }

    /**
     * 任务二: 对外实现
     */



}
